package com.artnology.independence.model;



import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;

import com.artnology.independence.Activator;
import com.artnology.independence.model.relationships.DVReference;
import com.artnology.independence.views.DependencyView;

import edu.uci.ics.jung.graph.Graph;

public class DVPackage extends DVNode{
	
	static final String DEFAULT_PACKAGE_NAME = "(default package)";
	
	public Color getPackageColor() {
		return packageColor;
	}


	private Color packageColor;
	private String[] tokenizedNames = null;
	
	
	@Override
	public Shape getPaintShape(double width, double height) {
		double x = (-width / 2) * getDrawingScalingFactor();
		double y = (-height / 2) * getDrawingScalingFactor();
		double w = width * getDrawingScalingFactor();
		double h = height * getDrawingScalingFactor();
		return new Ellipse2D.Double( x - 15, y -2, w + 30, h + 3);

	}
	
	
	@Override
	public float getDrawingScalingFactor() {
		float scalingFactor = getContainedTypes().size()/20;
		if (scalingFactor < 1f) {
			scalingFactor = 1;
		}
		return scalingFactor;
	}

	
	private Set<DVType> containedTypes = new HashSet<DVType>();
	
	public boolean addContainedType(DVType type) {
		return containedTypes.add(type);
	}
	
	public boolean isTopLevelPackage() {
		return (getParentPackage()== getGraphModel().getRootPackage());
	}
	
	public List<DVPackage> flattenPackageTree() {
		List<DVPackage> allSubPackages = new ArrayList<DVPackage>();
		allSubPackages.add(this);
		collectSubPackages(this, allSubPackages);
		return allSubPackages;
	}
	
	public Collection<DVNode> getContainedTypesWithSubpackages() {
		Set<DVNode> allTypesWithSubpackages = new HashSet<DVNode>();
		for (DVPackage pack : flattenPackageTree()) {
			allTypesWithSubpackages.addAll(pack.getContainedTypes());
		}
		return allTypesWithSubpackages;
	}
	
	public int getNumberOfContainedTypesWithSubpackages() {
		return getContainedTypesWithSubpackages().size();
	}
	
	private static final void collectSubPackages(DVPackage currentParentPackage, List<DVPackage> allSubPackages) {
		for (DVPackage dvPackage : currentParentPackage.getChildren()) {
			allSubPackages.add(dvPackage);
			collectSubPackages(dvPackage, allSubPackages);
		}
	}
	
	public DVPackage(String name, DVGraphModel model) {
		super(name, model);
		StringTokenizer st = new StringTokenizer(name, ".");
		tokenizedNames = new String[st.countTokens()];
		int i = 0;
		while (st.hasMoreTokens()) {
			tokenizedNames[i]= st.nextToken();
			i++;
		}
	}
	
	String[] getTokenizedNames() {
		return tokenizedNames;
	}
	

	public DVPackage getParentPackage() {
		DVPackage bestCandidate = getGraphModel().getRootPackage();
		int minimalDistance = Integer.MAX_VALUE;
		outer: for (DVPackage parentCandidate : getGraphModel().getAllPackages()) {
			int distance = getTokenizedNames().length - parentCandidate.getTokenizedNames().length;
			if (distance >= 1) {
				for (int i = 0; i < parentCandidate.getTokenizedNames().length; i++) {
					if (!parentCandidate.getTokenizedNames()[i].equals(getTokenizedNames()[i])) {
						continue outer;
					}
				}
				if (distance < minimalDistance){
					minimalDistance = distance;
					bestCandidate = parentCandidate;
				}
			} else {
				continue outer;
			}
		}
		return bestCandidate;
	}
	
	
	public Set<DVPackage> getChildren() {
		Set<DVPackage> children = new HashSet<DVPackage>();
		for (DVPackage childCandidate : getGraphModel().getAllPackages()) {
			if (childCandidate.getParentPackage().equals(this)) {
				children.add(childCandidate);
			}
		}
		return children;
	}
	
	public void setPackageColor(Color packageColor) {
		this.packageColor = packageColor;
	}

	public String getLabel() {
		return getName();
	}
	
	public Collection<DVType> getContainedTypes() {
		return containedTypes;
	}
	
	public boolean isContained(DVModelObject type) {
		return getContainedTypes().contains(type);
	}



	@Override
	public String toString() {
		String toString = super.toString();
		toString += "::#contained Types " + getContainedTypes().size();
		return toString;
	}
	

	@Override
	public Color getNodeFillColor() {
		if (Activator.getDefault().getPrefStore().getBoolean(DependencyView.SHOW_COUPLING)) {
			return getCouplingColorCode();
		} else {
			return packageColor;
		}

	}
	
	public Collection<DVType> getVisibleContainedTypes() {
		Set<DVType> visibleContainedTypes = new HashSet<DVType>();
		for (DVType dvNode : getContainedTypes()) {
			if (getGraphModel().getMyGraph().getGraph().containsVertex(dvNode)) {
				visibleContainedTypes.add(dvNode);
			}
		}
		return visibleContainedTypes;
		
	}
	
	public Set<DVType> getVisibleContainedTypesWithSubpackages() {
		Set<DVType> visibleContainedTypes = new HashSet<DVType>();
		collectVisibleContainedTypes(visibleContainedTypes, this);
		return visibleContainedTypes;
		
	}
	
	private final void collectVisibleContainedTypes(Set<DVType> containedTypes, DVPackage parentPackage) {
		containedTypes.addAll(parentPackage.getVisibleContainedTypes());
		for (DVPackage pack : parentPackage.getChildren()) {
			collectVisibleContainedTypes(containedTypes, pack);
		}
	}
	
	
	public boolean hasNoVisibleContainedType() {
		for (DVNode dvNode : getContainedTypes()) {
			if (getGraphModel().getMyGraph().getGraph().containsVertex(dvNode)) {
				return false;
			}
		}
		return true;
	}
	
	public static final int getNumberOfCurrentlyInvolvedPackages(Graph<DVNode, DVReference> currentGraph) {
		Set<DVPackage> involvedPackages = new HashSet<DVPackage>();
		for (DVModelObject node : currentGraph.getVertices()) {
			if (node instanceof DVType) {
				involvedPackages.add(((DVType) node).getPackage());
			} else {
				involvedPackages.add((DVPackage) node);
			}
		}
		return involvedPackages.size();
	}
	
	
	
	
}
